mod json_helper;
use json_helper::JsonHelper;
use serde_json::{json, Value};

const DYNAMIC_IP: &str = "动态获取ip地址";

fn main() {
  let mut config_file = std::env::current_exe().unwrap();
  config_file.set_extension("json");
  if !config_file.exists() {
    config_file = std::path::PathBuf::from("setip.json");
  }

  println!("读入配置文件：{config_file:?}\n");

  let config: Value = json_helper::file_to_json(&config_file).unwrap_or_else(|| {
    let default = json!({
        "adapter": "以太网",
        "sets": [
            {
                "ip_address": "192.168.100.157",
                "subnet_mask": "255.255.255.0",
                "default_gateway": "192.168.100.1",
                "dns_server1": "202.112.170.10",
                "dns_server2": ""
            },
        ]
    });
    if let Err(e) = json_helper::json_to_file(&default, &config_file) {
      println!("写{:?}发生错误:{:?}", config_file, e);
    }
    default
  });
  let adapter = config["adapter"].str("以太网");
  let sets: &Vec<Value> = config["sets"].as_array().unwrap();
  let mut ips: Vec<&str> = sets.iter().map(|ip| ip["ip_address"].str("")).collect();
  ips.push(DYNAMIC_IP);

  let joined = ips
    .iter()
    .enumerate()
    .map(|(i, ip)| format!("    {}. {}", i, ip))
    .collect::<Vec<_>>()
    .join("\n");

  let msg=format!(
    "下面将切换网卡{adapter}的ip地址:\n{joined}\n请输入数字 0-{} 选择ip，输入其他值显示当前可用网卡名称:",
    ips.len() - 1
  );
  let choice = prompt(&msg);
  let choice = choice.trim().parse::<usize>();

  let ip = match choice {
    Ok(i) if i < ips.len() => ips[i],
    _ => "",
  };

  if ip.is_empty() {
    let adapters = get_network_interfaces();
    println!("一共发现 {} 块网卡:", adapters.len());
    adapters.iter().enumerate().for_each(|(i, a)| {
      println!("  {}. {}", i + 1, a);
    });
  } else if ip == DYNAMIC_IP {
    set_network_dynamic(adapter).unwrap();
  } else {
    let i = choice.unwrap();
    let set = sets[i].clone();
    set_network_static(
      adapter,
      ip,
      set["subnet_mask"].str(""),
      set["default_gateway"].str(""),
      set["dns_server1"].str(""),
      set["dns_server2"].str(""),
    )
    .unwrap();
  }
  prompt("\n按回车键退出!");
}

fn prompt(msg: &str) -> String {
  println!("{msg}");
  let mut choice = String::new();
  std::io::stdin()
    .read_line(&mut choice)
    .expect("读取键盘输入出错");
  choice
}

pub fn execute<I, S>(program: &str, args: I) -> Result<String, String>
where
  I: IntoIterator<Item = S>,
  S: AsRef<std::ffi::OsStr>,
{
  match std::process::Command::new(program)
    .args(args)
    .output()
    .map(|output| {
      let (out, _, _) = encoding_rs::GB18030.decode(&output.stdout);
      (String::from(out), output.status)
    })
    .map_err(|e| format!("{} error: {}", program, e))
  {
    Ok((output, status)) if status.success() => Ok(output),
    Ok((_, status)) => Err(format!("{} exit code: {}", program, status)),
    Err(e) => Err(e),
  }
}

fn get_network_interfaces() -> Vec<Adapter> {
  let output = execute("netsh", ["interface", "show", "interface"])
    .expect("failed to execute netsh show interface");
  output
    .lines()
    .filter(|line| !line.is_empty())
    .skip(2)
    .map(Adapter::new)
    .collect()
}

pub fn set_network_static(
  adapter: &str, ip_address: &str, subnet_mask: &str, default_gateway: &str, dns_server1: &str,
  dns_server2: &str,
) -> Result<(), String> {
  execute(
    "netsh",
    [
      "interface",
      "ipv4",
      "set",
      "address",
      &format!("name={adapter}"),
      "source=static",
      &format!("address={ip_address}"),
      &format!("mask={subnet_mask}"),
      &format!("gateway={default_gateway}"),
    ],
  )?;

  execute(
    "netsh",
    [
      "interface",
      "ipv4",
      "delete",
      "dnsservers",
      &format!("name={}", adapter),
      "all",
    ],
  )?;

  execute(
    "netsh",
    [
      "interface",
      "ipv4",
      "add",
      "dnsservers",
      &format!("name={adapter}"),
      &format!("address={dns_server1}"),
      "index=1",
    ],
  )?;
  if !dns_server2.is_empty() {
    execute(
      "netsh",
      [
        "interface",
        "ipv4",
        "add",
        "dnsservers",
        &format!("name={adapter}"),
        &format!("address={dns_server2}"),
        "index=2",
      ],
    )?;
  }
  println!(
    "已将 {} ipv4地址设置为 {} {} {} {} {}",
    adapter, ip_address, subnet_mask, default_gateway, dns_server1, dns_server2
  );
  Ok(())
}

pub fn set_network_dynamic(adapter: &str) -> Result<(), String> {
  execute(
    "netsh",
    [
      "interface",
      "ipv4",
      "set",
      "address",
      &format!("name={}", adapter),
      "source=dhcp",
    ],
  )?;

  execute(
    "netsh",
    [
      "interface",
      "ipv4",
      "set",
      "dnsservers",
      &format!("name={}", adapter),
      "source=dhcp",
    ],
  )?;
  println!("已将 {} ipv4地址设置为动态获取", adapter);
  Ok(())
}

#[derive(Debug)]
pub struct Adapter {
  pub enabled: bool,
  pub connected: bool,
  pub interface_type: String,
  pub interface_name: String,
}
impl Adapter {
  fn new(info: &str) -> Self {
    let mut flds = info.split_whitespace();
    let enabled = if let Some(fld) = flds.next() {
      fld == "已启用"
    } else {
      false
    };
    let connected = if let Some(fld) = flds.next() {
      fld == "已连接"
    } else {
      false
    };
    let interface_type = if let Some(fld) = flds.next() {
      fld.to_owned()
    } else {
      "未知类型".to_owned()
    };
    let interface_name = if let Some(fld) = flds.next() {
      fld.to_owned()
    } else {
      "未知名称".to_owned()
    };
    Self {
      enabled,
      connected,
      interface_type,
      interface_name,
    }
  }
}
impl std::fmt::Display for Adapter {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(
      f,
      "名称为【{}】，处于{}用状态，线路{}连接",
      self.interface_name,
      if self.enabled { "启" } else { "禁" },
      if self.connected { "已" } else { "未" }
    )
  }
}
